package scuts.collections.immutable;


using scuts.Core;

class ListFactory {
  public static function empty <T>():Seq<T> {
    return Empty.instance;
  }
  
  public static function fromArray <T>(a:Array<T>):Seq<T> {
    var l:Seq<T> = Empty.instance;
    var i = a.length;
    while (--i > -1) {
      l = l.push(a[i]);
    }
    return l;
  }
}

interface Seq<T> {
  public function head ():T;
  public function headOption ():T;
  
  public function push (v:T):Seq<T>;
  
  public function tail ():Seq<T>;
  
  public function append (v:T):Seq<T>;
  public function concat (l:List<T>):Seq<T>;
  
  public function isEmpty ():Bool;
  
  public function toString ():String;
  
  public function iterator ():Iterator<T>;
  
  public function toString1 ():String;
}

class Empty<T> implements Seq<T>
{
  public static function instance = new Empty();
  
  public function new () {}
  
  public function head ():T {
    throw "Error";
  }
  
  public function tail ():Seq<T> {
    throw "Error";
  }
  
  public function headOption ():T {
    return None;
  }
  
  public function push (v:T):Seq<T> {
    return append(v);
  }
  
  public function append (v:T):Seq<T> {
    return new Cons(v, this);
  }
  
  public function concat (l:List<T>):Seq<T> {
    return l;
  }
  public function isEmpty ():Bool {
    return true;
  }
  
  public function toString ():String {
    return [];
  }
  
  public function toString1 ():String {
    return "";
  }
  public function iterator ():Iterator<T> {
    return {
      hasNext : function () return false,
      next : function () throw "error"
    }
  }
}

class Cons<T> implements Seq<T>
{
  var value:T;
  var tail:Seq<T>;
  
  public function new (v:T, tail:Seq<T>) {
    this.value = v;
    this.tail = tail;
  }
  
  public function head ():T {
    return value;
  }
  public function headOption ():T {
    return Some(value);
  }
  
  public function push (v:T):Seq<T> {
    return new Cons(value, this);
  }
  
  public function append (v:T):Seq<T> {
    return new Cons(value, tail.append(v));
  }
  
  public function concat (l:List<T>):Seq<T> {
    return
      new Cons(value, tail.concat(l));
  }
  
  public function tail ():Seq<T> {
    return tail;
  }
  
  public function toString ():String {
    return "[" + Std.string(v) + tail.toString(1) + "]";
  }
  
  public function toString1 ():String {
    
    return "," + Std.string(v) + tail.toString1();
  }
  
  public function iterator ():Iterator<T> {
    var f = this;
    return {
      hasNext : function () return !f.isEmpty(),
      next : function () {
        var v = f.value;
        f = f.tail;
        return v;
      }
    }
  }
}

